"""
The Robot Framework parser. parse robot output to dictionary which used to import result to test rail

"""

import re
from datetime import datetime
from lxml import etree



def parse(filename):
    """
    Parse Robot Framework test results.

    Versions tested to work:
     * 3.0.1
    """
    try:
        tree = etree.parse(filename)
    except etree.XMLSyntaxError as ex:
        raise Exception(ex)

    root = tree.getroot()
    if root.tag != 'robot':
        raise Exception('root.tag is not robot')
    test_results = []
    stack = root.xpath('./suite')
    while stack:
        tree = stack.pop()
        path = [p.get('name') for p in tree.iterancestors() if p.tag == 'suite']
        path = path[::-1]
        tests = tree.xpath('./test')
        author_node = tree.xpath('./meta')
        author = ""
        if author_node and author_node.__len__()>0 and author_node[0].text is not None:
            author = author_node[0].text.lower()
            if author is None:
                author = author_node[0].get("name").lower()
        case_id = "0"
        case_id_node = tree.xpath('./meta[@name="ID"]')
        if case_id_node and len(case_id_node)>0:
           case_id = case_id_node[0].text
           match = re.search(r'\d+', case_id)
           if match:
              case_id = match.group(0)
        parse_result = [_parse_test(t,case_id,author) for t in tests];
        if parse_result:
            test_results.append(parse_result[0])
        suites = tree.xpath('./suite')
        for suite in suites:
            stack.append(suite)
    return test_results

def _parse_test(test,case_id,author):
    test_name = test.get('name')
    # todo
    test_description = test.get('name')
    status_node = test.find("./status")
    test_status = status_node.get('status')
    test_status = get_status_id(test_status)
    test_starttime = status_node.get('starttime')
    test_starttime = datetime.strptime(test_starttime, '%Y%m%d %H:%M:%S.%f')
    test_exec_time = test_starttime.strftime('%H:%M:%S')
    test_exec_date = test_starttime.strftime('%Y-%m-%d')
    test_endtime = status_node.get('endtime')
    test_endtime = datetime.strptime(test_endtime, '%Y%m%d %H:%M:%S.%f')
    test_duration = (test_endtime - test_starttime).total_seconds()
    test_duration = int(test_duration)
    # test steps
    keywords = test.xpath('./kw')
    step_results = [_parse_step(k) for k in keywords]
    
    test_name = test_name.replace('${SPACE}',' ')
    message_text = filter_tags(status_node.text)
    message = ""
    if len(message_text.split("New message:"))>=2:
        message = message_text.split("New message:")[1] 
    else:
        message = message_text
    message = message.split("Old status")[0]
    defectId = ""
    documentation = test.xpath('./doc')
    if documentation and documentation.__len__()>0:
        defectId = documentation[0].text    
    return {
        'case_id': case_id,
        'case_name': test_name,
        'status_id': test_status,
        'steps': step_results,
        'err': message,
        'elapsed':test_duration,
        'defects':defectId,
        'img': "",
        'author':author
    }
    
def get_defect_id(docuemtation):
    match = re.findall('\d\d\d\d\d',docuemtation)
    if match:
        return match[0]
    else:
        return ""
        
def filter_tags(htmlstr):
    if None == htmlstr:
        return ""
    # remove CDATA
    htmlstr = replaceCharEntity(htmlstr)
    re_cdata = re.compile('//<!\[CDATA\[[^>]*//\]\]>', re.I)  # CDATA
    re_script = re.compile('<\s*script[^>]*>[^<]*<\s*/\s*script\s*>', re.I)  # Script
    re_style = re.compile('<\s*style[^>]*>[^<]*<\s*/\s*style\s*>', re.I)  # style
    re_br = re.compile('<br\s*?/?>')  # handle \n
    re_h = re.compile('</?\w+[^>]*>')  # HTML label
    re_comment = re.compile('<!--[^>]*-->')  # HTML comment
    re_stopwords = re.compile('\u3000')  # remove useless '\u3000'
    s = re_cdata.sub('', htmlstr)  # remove CDATA
    s = re_script.sub('', s)  # remove SCRIPT
    s = re_style.sub('', s)  # remote style
    s = re_br.sub('\n', s)  # replace "br" to "\n"
    s = re_h.sub('', s)  # remote html tag
    s = re_comment.sub('', s)  # remote html comment
    s = re_stopwords.sub('', s)
    # remove blank lines
    blank_line = re.compile('\n+')
    s = blank_line.sub('\n', s)
    s = replaceCharEntity(s).strip()  #
    return s

def replaceCharEntity(htmlstr):
    CHAR_ENTITIES = {'nbsp': ' ', '160': ' ',
                    '60': '<', '62': '>',
                    'amp': '&', '38': '&',
                    'quot': '"', '34': '"', }
    re_charEntity = re.compile(r'&#?(?P<name>\w+);')
    sz = re_charEntity.search(htmlstr)
    while sz:
        entity = sz.group()  
        key = sz.group('name') 
        try:
            htmlstr = re_charEntity.sub(CHAR_ENTITIES[key], htmlstr, 1)
            sz = re_charEntity.search(htmlstr)
        except KeyError:
            # replace with empty string
            htmlstr = re_charEntity.sub('', htmlstr, 1)
            sz = re_charEntity.search(htmlstr)
    return htmlstr
        
def _parse_step(step):
    kw_name = step.get('name')
    kws = kw_name.split('.')
    if len(kws) > 1:
        name = kws[1]
    else:
        name = kw_name
    kw_status_node = step.find('./status')
    kw_status = kw_status_node.get('status')
    kw_status = get_status_id(kw_status)
    starttime = kw_status_node.get('starttime')
    starttime = datetime.strptime(starttime, '%Y%m%d %H:%M:%S.%f')
    exec_date = datetime.strftime(starttime, '%Y-%m-%d')
    exec_time = datetime.strftime(starttime, '%H:%M:%S')
    args = step.xpath('./arguments/arg')

    descr = ''
    for arg in args:
        descr += 'Argument = ' + arg.text + '\n'
    return {
        'content': name,
        'status_id': kw_status
    }


'''
Convert robot status to testrail status code
'''
def get_status_id(status):
    list_1 = ['PASS', 'PASSED']
    list_2 = ['BLOCK','SKIPPED','SKIP']
    list_3 = ['UNTEST', None, 'NOT RUN']
    list_4 = ['RETEST', 'RETRY']
    list_5 = ['FAIL', 'FAILED']
    list_7 = ['ONHOLD', 'CANCELLED']
    status = status.upper()
    if status in list_1:
        return 1
    elif status in list_2:
        return 2
    elif status in list_3:
        return 3
    elif status in list_4:
        return 4
    elif status in list_5:
        return 5
    elif status in list_7:
        return 7
    else: print('Unknown status!')
